package com.lanchetech.service.impl;


import com.lanchetech.common.enums.*;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.job.FlashSaleActivityNotifyJob;
import com.lanchetech.job.RewardQueueExchangeJob;
import com.lanchetech.job.RoomStartNotifyJob;
import com.lanchetech.job.WaitPayJob;
import com.lanchetech.service.AsyncNotifyService;
import com.lanchetech.service.JpushService;
import com.lanchetech.service.QuartzService;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;


/*
 * Quartz 定时任务
 * */
@Service
public class QuartzServiceImpl implements QuartzService {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private Scheduler scheduler;

    @Autowired
    RoomMapper roomMapper;

    @Autowired
    OrdersMapper ordersMapper;

    @Autowired
    OrderSkuMapper orderSkuMapper;

    @Autowired
    RewardQueueMapper rewardQueueMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    CardFlowMapper cardFlowMapper;

    @Autowired
    JpushService jpushService;

    @Autowired
    SubscribeRecordMapper subscribeRecordMapper;

    @Autowired
    FlashSaleSpuMapper flashSaleSpuMapper;

    @Autowired
    FlashSaleActivityMapper flashSaleActivityMapper;

    @Autowired
    AsyncNotifyService asyncNotifyService;

    @Override
    public void createFlashSaleJob(FlashSaleActivity activity) {
        Calendar startTime = Calendar.getInstance();
        // 秒杀开始前10分钟，执行提醒工作
        startTime.setTime(activity.getStartTime());
        startTime.add(Calendar.MINUTE, -10);

        // Group是分组，组内的job的Identity和trigger的Identity，不可以一样
        JobDetail startJobDetail = JobBuilder.newJob(FlashSaleActivityNotifyJob.class)
                // 指定任务组名和任务名
                .withIdentity("FLASH_JOB" + activity.getId().toString(), "FLASH_SALE_NOTIFY")
                // 添加一些参数，执行的时候用
                .usingJobData("id", activity.getId().toString())
                .build();

        // 创建请假开始任务的触发器
        // 创建cron表达式指定任务执行的时间，由于请假时间是确定的，所以年月日时分秒都是确定的，这也符合任务只执行一次的要求。
        String startCron = String.format("%d %d %d %d %d ? %d",
                startTime.get(Calendar.SECOND),
                startTime.get(Calendar.MINUTE),
                startTime.get(Calendar.HOUR_OF_DAY),
                startTime.get(Calendar.DAY_OF_MONTH),
                startTime.get(Calendar.MONTH) + 1,
                startTime.get(Calendar.YEAR)
        );
        CronTrigger startCronTrigger = TriggerBuilder.newTrigger()
                // 指定触发器组名和触发器名
                .withIdentity("FLASH_TRIGGER" + activity.getId().toString(), "FLASH_SALE_NOTIFY")
                .withSchedule(CronScheduleBuilder.cronSchedule(startCron))
                .build();

        // 将job和trigger添加到scheduler里
        try {
            scheduler.scheduleJob(startJobDetail, startCronTrigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return;
    }

    @Override
    public void flashSaleNotify(Long flashSaleActivityId) {
        // TODO 通知所有订阅的人，秒杀即将开始了
        FlashSaleActivity flashSaleActivity = flashSaleActivityMapper.selectByPrimaryKey(flashSaleActivityId);
        List<SubscribeRecord> subscribeRecords = subscribeRecordMapper.findAllByActivityId(flashSaleActivityId);
        if (!CollectionUtils.isEmpty(subscribeRecords)) {
            List<Long> appNotify = new ArrayList<>();
            List<Long> wxAppletNotify = new ArrayList<>();
            subscribeRecords.stream().forEach(item -> {
                if (SubscribeClientEnum.APP.getClient().equals(item.getSubscribeClient())) {
                    appNotify.add(item.getUserId());
                }
                if (SubscribeClientEnum.WX_APPLET.getClient().equals(item.getSubscribeClient())) {
                    wxAppletNotify.add(item.getUserId());
                }
            });

            //todo
            if (!CollectionUtils.isEmpty(appNotify)) {
                List<List<Long>> list = splitAry(appNotify, 10);
                list.stream().forEach(item -> {
                    if (!CollectionUtils.isEmpty(item)) {
                        asyncNotifyService.asyncAppNotify(item, flashSaleActivity.getName());
                    }
                });
            }
            if (!CollectionUtils.isEmpty(wxAppletNotify)) {
                List<List<Long>> list = splitAry(wxAppletNotify, 10);
                list.stream().forEach(item -> {
                    if (!CollectionUtils.isEmpty(item)) {
                        asyncNotifyService.asyncMiniAppNotify(item, flashSaleActivity);
                    }
                });
            }
        }
    }

    private static List<List<Long>> splitAry(List<Long> arg, int subSize) {
        // 1、先生成指令组数
        List<List<Long>> list = new ArrayList<>();
        for (int i = 0; i < subSize; i++) {
            list.add(new ArrayList<>());
        }
        // 2、将元素放入组内
        for (int i = 0; i < arg.size(); i++) {
            list.get(i % subSize).add(arg.get(i));
        }
        return list;
    }

    @Override
    public void modifyJobTime(String triggerName, String triggerGroupName, String cron) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                log.info("未找到对应的定时任务{},{}", triggerName, triggerGroupName);
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(cron)) {
                // 触发器
                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
                // 触发器名,触发器组
                triggerBuilder.withIdentity(triggerName, triggerGroupName);
                triggerBuilder.startNow();
                // 触发器时间设定
                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
                // 创建Trigger对象
                trigger = (CronTrigger) triggerBuilder.build();
                // 方式一 ：修改一个任务的触发时间
                scheduler.rescheduleJob(triggerKey, trigger);
                log.info("已修改定时任务{},{}", triggerName, triggerGroupName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void createRoomJob(Room room) {
        Calendar startTime = Calendar.getInstance();
        // 直播开始前5分钟，执行提醒工作
        startTime.setTime(room.getStartTime());
        startTime.add(Calendar.MINUTE, -5);

        // Group是分组，组内的job的Identity和trigger的Identity，不可以一样
        JobDetail startJobDetail = JobBuilder.newJob(RoomStartNotifyJob.class)
                // 指定任务组名和任务名
                .withIdentity("ROOM_JOB" + room.getId().toString(), "ROOM_START_NOTIFY")
                // 添加一些参数，执行的时候用
                .usingJobData("id", room.getId().toString())
                .build();

        // 创建请假开始任务的触发器
        // 创建cron表达式指定任务执行的时间，由于请假时间是确定的，所以年月日时分秒都是确定的，这也符合任务只执行一次的要求。
        String startCron = String.format("%d %d %d %d %d ? %d",
                startTime.get(Calendar.SECOND),
                startTime.get(Calendar.MINUTE),
                startTime.get(Calendar.HOUR_OF_DAY),
                startTime.get(Calendar.DAY_OF_MONTH),
                startTime.get(Calendar.MONTH) + 1,
                startTime.get(Calendar.YEAR)
        );
        CronTrigger startCronTrigger = TriggerBuilder.newTrigger()
                // 指定触发器组名和触发器名
                .withIdentity("ROOM_TRIGGER" + room.getId().toString(), "ROOM_START_NOTIFY")
                .withSchedule(CronScheduleBuilder.cronSchedule(startCron))
                .build();

        // 将job和trigger添加到scheduler里
        try {
            scheduler.scheduleJob(startJobDetail, startCronTrigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return;
    }

    @Override
    public void roomStartNotify(Long valueOf) {
        //todo 直播间开始
        roomMapper.updateStatusById(RoomStatusEnum.OPEN.getStatus(), valueOf);
    }

    @Override
    public void createWaitPayJob(Orders order) {
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(new Date());
        startTime.add(Calendar.MINUTE, 60);
        JobDetail startJobDetail = JobBuilder.newJob(WaitPayJob.class)
                // 指定任务组名和任务名
                .withIdentity("WAIT_PAY_JOB" + order.getId().toString(), "WAIT_PAY")
                // 添加一些参数，执行的时候用
                .usingJobData("orderId", order.getId())
                .build();
        // 创建请假开始任务的触发器
        // 创建cron表达式指定任务执行的时间，由于请假时间是确定的，所以年月日时分秒都是确定的，这也符合任务只执行一次的要求。
        String startCron = String.format("%d %d %d %d %d ? %d",
                startTime.get(Calendar.SECOND),
                startTime.get(Calendar.MINUTE),
                startTime.get(Calendar.HOUR_OF_DAY),
                startTime.get(Calendar.DAY_OF_MONTH),
                startTime.get(Calendar.MONTH) + 1,
                startTime.get(Calendar.YEAR)
        );
        CronTrigger startCronTrigger = TriggerBuilder.newTrigger()
                // 指定触发器组名和触发器名
                .withIdentity("ORDER_PAY_TRIGGER" + order.getId().toString(), "WAIT_PAY")
                .withSchedule(CronScheduleBuilder.cronSchedule(startCron))
                .build();

        // 将job和trigger添加到scheduler里
        try {
            scheduler.scheduleJob(startJobDetail, startCronTrigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void closeOrder(Long id) {
        Orders target = ordersMapper.selectByPrimaryKey(id);

        target.setCloseTime(new Date());
        target.setStatus(OrdersStatusEnum.CLOSE.getStatus());
        ordersMapper.updateByPrimaryKey(target);

        // orderSku也变更状态
        orderSkuMapper.updateStatusByOrderId(OrdersStatusEnum.CLOSE.getStatus(), id);

        //修改
    }

    @Override
    public void rewardQueueExchange(Long id) {
        RewardQueue rewardQueue = rewardQueueMapper.selectByPrimaryKey(id);
        if (RewardQueueStatusEnum.QUEUING.getStatus().equals(rewardQueue.getQueueStatus()) && RewardReceiveStatusEnum.NO_RECEIVED.getStatus().equals(rewardQueue.getRewardStatus())) {
            rewardQueue.setQueueStatus(RewardQueueStatusEnum.EXCHANGE.getStatus());
            rewardQueue.setRewardStatus(RewardReceiveStatusEnum.RECEIVED.getStatus());
            rewardQueueMapper.updateByPrimaryKey(rewardQueue);

            BigDecimal amount = rewardQueue.getAmount().divide(new BigDecimal(rewardQueue.getAmount().compareTo(BigDecimal.valueOf(2000)) > 0 ? 1 : 1.211), 2, BigDecimal.ROUND_HALF_DOWN);
            Long userId = rewardQueue.getUserId();
            if (amount.compareTo(BigDecimal.ZERO) == 0) {
                return;
            }
            BigDecimal cardPoint = userMapper.selectByPrimaryKey(userId).getCardPoint().add(amount);
            userMapper.updateCardPointById(cardPoint, userId);

            cardFlowMapper.insert(CardFlow.builder()
                    .userId(userId)
                    .amount(amount)
                    .tradeType(CardTradeTypeEnum.SYSTEM_GIVING.getTradeType())
                    .type(CardTradeTypeEnum.SYSTEM_GIVING.getType())
                    .cardPoint(cardPoint)
                    .remark("免单奖励嗨币")
                    .relateId(id)
                    .createdAt(new Date())
                    .build());
        }
    }

    @Override
    public void createRewardQueueExchangeJob(RewardQueue rewardQueue) {
        Calendar startTime = Calendar.getInstance();
        startTime.setTime(new Date());
        startTime.add(Calendar.YEAR, 1);
        JobDetail startJobDetail = JobBuilder.newJob(RewardQueueExchangeJob.class)
                // 指定任务组名和任务名
                .withIdentity("REWARD_QUEUE_EXCHANGE_JOB" + rewardQueue.getId().toString(), "REWARD_QUEUE_EXCHANGE")
                // 添加一些参数，执行的时候用
                .usingJobData("id", rewardQueue.getId())
                .build();
        // 创建请假开始任务的触发器
        // 创建cron表达式指定任务执行的时间，由于请假时间是确定的，所以年月日时分秒都是确定的，这也符合任务只执行一次的要求。
        String startCron = String.format("%d %d %d %d %d ? %d",
                startTime.get(Calendar.SECOND),
                startTime.get(Calendar.MINUTE),
                startTime.get(Calendar.HOUR_OF_DAY),
                startTime.get(Calendar.DAY_OF_MONTH),
                startTime.get(Calendar.MONTH) + 1,
                startTime.get(Calendar.YEAR)
        );
        CronTrigger startCronTrigger = TriggerBuilder.newTrigger()
                // 指定触发器组名和触发器名
                .withIdentity("REWARD_QUEUE_EXCHANGE_TRIGGER" + rewardQueue.getId().toString(), "REWARD_QUEUE_EXCHANGE")
                .withSchedule(CronScheduleBuilder.cronSchedule(startCron))
                .build();

        // 将job和trigger添加到scheduler里
        try {
            scheduler.scheduleJob(startJobDetail, startCronTrigger);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}